#ifndef __CAVA_H__
#define __CAVA_H__
#include <vector>
#include <string>

namespace cavascript{
    using namespace std;

	namespace engine{
		enum EngineType {V8, SPIDEMONKEY};
	}
    template <class T, engine::EngineType engineType= engine::V8>
    class Class
    {
    public:
        
        static Class& instance(const char* name);
        
        template <class U>
        Class& inherit(Class<U, engineType>& p);

        template <class TField>
        Class& bind(TField T::*field,
                           const char* name);

        Class& ctor();

        template <class A1>
        Class& ctor(A1);
        
        template <class A1, class A2>
        Class& ctor(A1, A2);

        template <class R>
        Class& bind(R (T::*function)(),
                            const char* name);

        template <class R,
                  class A1>
        Class& bind(R (T::*function)(A1),
                            const char* name);

        template <class R,
                  class A1,
                  class A2>
        Class& bind(R (T::*function)(A1, A2),
                            const char* name);
    };


	
///////////////////////////////////////////////////////////////////////////////
#ifndef JAVASCRIPT_ENGINE_DISABLE_V8
#include <v8.h>
using namespace v8;

    template <class T>
    class Class<T, engine::V8>
	{
        
        typedef Class<T, engine::V8> ProxyType;
        typedef std::pair<Handle<FunctionTemplate>, Handle<ObjectTemplate> TemplateType;
        typedef std::map<InvocationCallback, TemplateType> TemplateMap;

		struct FieldAccessorInterface
		{
			virtual void set(Local<String> name,
                             Local<Value> value,
                             const AccessorInfo& info) = 0;

			virtual Handle<Value> get(Local<String> name,
			                          const AccessorInfo& info) = 0;
		};

        struct CallBackInterface
        {
            virtual Handle<Value> call(const Arguments& args) = 0;
        };

        static FieldAccessorInterface* getAccessor(const AccessorInfo& info)
        {
            Local<External> external;
            external = Local<External>::Cast(info.Data());
            return static_cast<FieldAccessorInterface*>(external->Value());
        }

        static void FieldSetter(Local<String> name,
            Local<Value> value,
            const AccessorInfo& info)
        {
            getAccessor(info)->set(name, value, info);
        }

        static Handle<Value> FieldGetter(Local<String> name,
            const AccessorInfo& info)
        {
            return getAccessor(info)->get(name,  info);
        }

        static Handle<Value> CallBackHandler(const Arguments& args)
        {
            Local<External> external;
            external = Local<External>::Cast(args.Data());
            CallBackInterface* callback;
            callback = static_cast<CallBackInterface*>(external->Value());
            return callback->call(args);
        }

        static T* getHolder(const AccessorInfo& info)
        {
            Local<External> external;
            Local<Object> holder = info.Holder();
            external = Local<External>::Cast(holder->GetInternalField(0));
            return static_cast<T*>(external->Value());  
        }

        static T* getHolder(const Arguments& args)
        {
            Local<Value> value = args.Holder()->GetInternalField(0);
            Local<External> external = Local<External>::Cast(value);
            return static_cast<T*>(external->Value());  
        }

        template <class FieldType>
		struct FieldAccessor : public FieldAccessorInterface
		{
            typedef FieldType T::*TFieldType;
            FieldAccessor(TFieldType f)
                :_field(f){}

			virtual void set(Local<String> name,
                             Local<Value> value,
                             const AccessorInfo& info)
			{
                ProxyType::getHolder(info)->*_field = Cast<FieldType>((FieldType*)NULL, value);
			}

			virtual Handle<Value> get(Local<String> name,
                                      const AccessorInfo& info)
			{
                return Cast((ProxyType::getHolder(info)->*_field));
			}

            TFieldType _field;
		};


    public:
		static Class& instance(const char* name)
		{
            String className = String::New(name); 
            if (_instances.find(className) == _instances.end())
            {
                _instances[className]._className = className;
            }
            return _instances[className];
		}
        
        Class& addConstructor(InvocationCallback creator)
        {
            if (_templates.find(creator) == _templates.end())
            {
                TemplateType& t = _templates[creator];
			    
                t->first = FunctionTemplate::New(creator);
			    t->first->SetClassName(_className);

			    t->second = t->first->InstanceTemplate();
			    t->second->SetInternalFieldCount(1); 
            }
			return *this;
        }

        Class& ctor()
        {
            return addConstructor(Create<T>);
        }

        template <class A1>
        Class& ctor(A1 a1)
        {
            return addConstructor(Create<T, A1>);
        }
        //TODO.. more ctors..
		

        template <class TField>
		Class& bind(TField T::*field,
                           const char* name)
		{
            ProxyType::FieldAccessor<TField>* accessor(0);
            accessor = new ProxyType::FieldAccessor<TField>(field);
            _resources.push_back(accessor);

			Handle<Value> data = External::New(accessor);

			_objectTemplate->SetAccessor(String::New(name),
				                         FieldGetter,
										 FieldSetter,
										 data);
			return *this;
		}



        void addMethod(String name, Local<FunctionTemplate> func)
        {
            for (TemplateMap::iterator iter =  _templates.begin();
                                       iter != _templates.end();
                                       iter++)
            {
                iter->second->Set(name, func);
            }
        }
 
        Class& bind(CallBackInterface* callback,
                            const char* name)
        {
            Handle<Value> data = External::New(callback);
            Local<FunctionTemplate> func = FunctionTemplate::New();
            func->SetCallHandler(CallBackHandler,data);  
            addMethod(name, func); 
            return *this;
        }


        template<class TReturn>
        struct CallBackFunction : public CallBackInterface
        {
            typedef TReturn (T::*FunctionType)();
            CallBackFunction(FunctionType f)
                :_f(f){}
            Handle<Value> call(const Arguments& args)
            {
                T* t = ProxyType::getHolder(args);  
                ProxyType::Cast((t->*_f)());
            }
            FunctionType _f;
        };

        template<>
        struct CallBackFunction<void> : public CallBackInterface
        {
            typedef void (T::*FunctionType)();
            CallBackFunction(FunctionType f)
                :_f(f){}

            Handle<Value> call(const Arguments& args)
            {
                T* t = ProxyType::getHolder(args);  
                (t->*_f)();
                return v8::Undefined();
            }
            FunctionType _f;
        };

        template<class TReturn, class A1>
        struct CallBackFunction1 : public CallBackInterface
        {
            typedef TReturn (T::*FunctionType)(A1);
            CallBackFunction1(FunctionType f)
                :_f(f){}
            Handle<Value> call(const Arguments& args)
            {
                T* t = ProxyType::getHolder(args);  
                if (args.Length() > 1)
                    return v8::Undefined();

                return ProxyType::Cast((t->*_f)(Cast((A1*)NULL, args[0])));
            }
            FunctionType _f;
        };

        template<class A1>
        struct CallBackFunction1<void, A1> : public CallBackInterface
        {
            typedef void (T::*FunctionType)(A1);
            CallBackFunction1(FunctionType f)
                :_f(f){}
            Handle<Value> call(const Arguments& args)
            {
                T* t = ProxyType::getHolder(args);  
                if (args.Length() == 1)
                    (t->*_f)(Cast((A1*)NULL, args[0]));
                return v8::Undefined();
            }
            FunctionType _f;
        };

        template<class TReturn, class A1, class A2>
        struct CallBackFunction2 : public CallBackInterface
        {
            typedef TReturn (T::*FunctionType)(A1, A2);
            explicit CallBackFunction2(FunctionType f)
                :_f(f){}
            Handle<Value> call(const Arguments& args)
            {
                T* t = ProxyType::getHolder(args);  
                if (args.Length() != 2)
                    return v8::Undefined();

                return ProxyType::Cast((t->*_f)(Cast((A1*)NULL, args[0]),
                                                Cast((A2*)NULL, args[1])));
            }
            FunctionType _f;
        };

        template<class A1, class A2>
        struct CallBackFunction2<void, A1, A2> : public CallBackInterface
        {
            typedef void (T::*FunctionType)(A1, A2);
            explicit CallBackFunction2(FunctionType f)
                :_f(f){}
            Handle<Value> call(const Arguments& args)
            {
                T* t = ProxyType::getHolder(args);  
                if (args.Length() == 2)
                    (t->*_f)(Cast((A1*)NULL, args[0]),
                             Cast((A2*)NULL, args[1]));
                return v8::Undefined();
            }
            FunctionType _f;
        };

        template <class R>
		Class& bind(R (T::*function)(),
                            const char* name)
        {
            CallBackInterface* callback;
            callback = new CallBackFunction<R>(function);
            _resources.push_back(callback);
            return bind(callback, name);
        }

		template <class R,
		          class A1>
	    Class& bind(R (T::*function)(A1), const char* name)
        {
            CallBackInterface* callback;
            callback = new CallBackFunction1<R, A1>(function);
            _resources.push_back(callback);
            return bind(callback, name);
        }

		template <class R,
                  class A1, 
                  class A2>
		ProxyType bind(R (T::*function)(A1, A2), const char* name)
                  {
                      CallBackInterface* callback;
                      callback = new CallBackFunction2<R,A1,A2>(function);
                      _resources.push_back(callback);
                      return bind(callback, name);
                  }

        template<class U>
        Handle<Value> AddObject(U* u)
        {
            HandleScope scope;
            Handle<ObjectTemplate> templ = _objectTemplate;
            Handle<Object> result = templ->NewInstance();
            Local<External> external;

            external = Local<External>::Cast(result->GetInternalField(0));
            return scope.Close(result);
        }
        
        static Handle<Value> Cast(bool v)     { return Boolean::New(v); }	
        static Handle<Value> Cast(double v)   { return Number::New(v); }  
        static Handle<Value> Cast(uint32_t v) { return Uint32::New(v); }	
        static Handle<Value> Cast(int32_t  v) { return Int32::New(v); }

        template<class U>
        static Handle<Value> Cast(U* u) 
        { 
            return Class<U, engine::V8>::instance().AddObject(u);
        };

        template<class U>
        static Handle<Value> Cast(U& u) 
        { 
            return Class<U, engine::V8>::instance().AddObject(&u);
        };
        
        static bool Cast(bool*, Local<Value> value) 
        { 
            return value->BooleanValue(); 
        }	

        static double Cast(double*, Local<Value> value) 
        { 
            return value->NumberValue(); 
        }	

        static uint32_t Cast(uint32_t*, Local<Value> value) 
        { 
            return value->Uint32Value(); 
        }	

        static int32_t  Cast(int32_t*, Local<Value> value) 
        { 
            return value->Int32Value(); 
        }

        static int64_t  Cast(int64_t*, Local<Value> value) 
        { 
            return value->IntegerValue(); 
        }

        static Handle<Value> Cast(Handle<Value> value) 
        { 
            return value;
        };


        template<class U>
        static U Cast(U*, Local<Value> value) 
        { 
            //return Class<U, engine::V8>::instance().Object(value);
            //TODO
        }

        template<class U>
        static U Cast(U&, Local<Value> value) 
        { 
            //return Class<U, engine::V8>::instance().UnwrapObject(value);
        }

        Class(){}
        ~Class()
        {
            std::vector<void*>::iterator it;
            for (it = _resources.begin(); it != _resources.end(); it++)
            {
                delete *it;
            }
            _resources.clear();
        }

        static std::map<String, Class<T, engine::V8> > _instances;
        static std::vector<void*> _resources;
		
        Local<String> _className;
        std::map<InvocationCallback, std::pair<Handle<FunctionTemplate>, Handle<ObjectTemplate> > _templates;
	};

	template<class T>
    std::map<String, Class<T, engine::V8> > Class<T, engine::V8>::_instances;
    
    template<class T>
    std::vector<void*> Class<T, engine::V8>::_resources;
}
#endif

#endif //__ECMASCRIPT_ENGINE_H__